package com.ruoran.user.dao;

import com.ruoran.enums.CardType;
import com.ruoran.user.CountChangeListener;
import com.ruoran.user.domain.Card;
import com.ruoran.user.domain.User;
import com.ruoran.user.util.Page;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Repository
public class UserDAO {

    private static final String PASSWORD = "$2a$10$AQ45Ob2bPp9xfzf./VGLqOoNUv5U2Pm8MniLdGkHrfJ/ORsFI1yei";
    public static Long initRecords = 10000L;

    protected static Map<Long, User> database = new ConcurrentHashMap<>();
    protected static Map<String, User> databaseCache = new ConcurrentHashMap<>();

    static String xing = "王李张刘陈杨黄赵吴周徐孙马朱胡郭何高林罗郑梁谢宋唐许韩冯邓曹彭曾萧田董潘袁于蒋蔡余杜叶程苏魏吕丁任沈姚卢姜崔钟谭陆汪范金石廖贾夏韦傅方白邹孟熊秦邱江尹薛阎段雷侯龙史陶黎贺顾毛郝龚邵万钱严覃武戴莫孔向汤";
    static String[] femaleName = {"正", "阳", "斌", "龙", "旭", "林", "熊", "兵", "威", "伟", "志", "武", "栋", "虎", "凯", "潇", "子轩", "超", "新余", "静蕾", "磊", "刚", "正宇", "晨", "雷", "天赐", "辰", "康", "顺"};
    static String[] maleName = {"玫", "琪", "琼", "文文", "欣欣", "楠", "贝贝", "娜娜", "静静", "婷婷", "丹丹", "蕾蕾", "蓓蓓", "紫萱", "馨予", "静蕾", "梅", "媚", "丽丽", "莉莉", "佳", "晓雪", "超越", "梦洁", "依娜", "漫妮", "雨婷", "雅芙", "雨嘉", "凌薇", "月婵", "优璇", "娅楠", "美莲", "可馨", "嫦曦", "茹雪", "明美", "惠茜", "香茹"};
    static String letters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    static SecureRandom random = new SecureRandom();

    static {
        init(initRecords);
    }

    private CountChangeListener listener;

    public static void init(long total) {
        database.clear();
        for (int i = 1; i <= total; i++) {
            Long id = (long) i;
            short gender = (short) (random.nextInt(2) + 1);
            User user = new User(id, getUsername(), PASSWORD, getName(gender), random.nextBoolean(), i * 1000F + random.nextDouble(), gender, random.nextFloat() * 1000);
            long birthday = getTime();
            user.setBirthday(new Date(birthday));
            user.setAge(Math.round((new Date().getTime() - birthday) / 1000F / 3600 / 24 / 365));
            user.setCreateTime(LocalDateTime.now());
            String lng = String.valueOf(random.nextInt(200) + Math.random());
            String lat = String.valueOf(random.nextInt(200) + Math.random());
            user.setLng(new BigDecimal(lng));
            user.setLat(new BigDecimal(lat));

            Card card = new Card();
            card.setId(i);
            card.setType(CardType.ID_CARD.getType());
            card.setName(CardType.ID_CARD.getName());
            card.setIssueDate(new Date(user.getBirthday().getTime() + 16L * 365 * 24 * 3600 * 1000));
            card.setExpiringDate(new Date(card.getIssueDate().getTime() + 5L * 365 * 24 * 3600 * 1000));
            card.setNumber("420321" + new SimpleDateFormat("yyyyMMdd").format(user.getBirthday()) + String.format("%04d", random.nextInt(2000)));
            user.setCard(card);

            database.put(id, user);
        }

        database.get(1L).setUsername("admin");
        database.get(2L).setUsername("super");
        database.get(3L).setUsername("iloveyou");
        database.get(4L).setUsername("forgetme");
        database.get(5L).setUsername("guest");
        database.get(6L).setUsername("forever");
        database.get(7L).setUsername("alone");
        database.get(8L).setUsername("number9");
    }

    static String getName(short gender) {
        return xing.charAt(random.nextInt(xing.length())) + (gender == 2 ? maleName[random.nextInt(maleName.length)] : femaleName[random.nextInt(femaleName.length)]);
    }

    static String getUsername() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            sb.append(letters.charAt(random.nextInt(letters.length())));
        }
        return sb.toString();
    }

    static long getTime() {
        return (long) (random.nextDouble() * 1000000000000L + (random.nextInt(3) % 2 == 0 ? random.nextInt(100000) : random.nextInt(10000000)));
    }

    public Page<User> queryForAll() {
        return this.queryForPage(1, this.count().intValue());
    }

    public Page<User> queryForPage(int pageNum, int pageSize) {
        List<User> data = new ArrayList<>();

        List<Long> ids = determineDataRange(pageNum, pageSize);
        for (Long id : ids) {
            data.add(database.get(id));
        }

        Page<User> page = new Page<>();
        page.setPageNum(pageNum);
        page.setPageSize(pageSize);
        page.setRecords(this.count());
        page.setResult(data);
        return page;
    }

    public List<User> queryList(int pageNum, int pageSize) {
        List<User> data = new ArrayList<>();

        List<Long> ids = determineDataRange(pageNum, pageSize);
        for (Long id : ids) {
            data.add(database.get(id));
        }

        return data;
    }

    private List<Long> determineDataRange(int pageNum, int pageSize) {
        List<Long> ids = new ArrayList<>(database.keySet());
        if (pageNum < 1) pageNum = 1;
        int start = (pageNum - 1) * pageSize;
        if (start > ids.size()) start = ids.size() - pageSize;
        int end = Math.min(pageNum * pageSize, ids.size());
        return ids.subList(start, end);
    }

    public User find(Long id) {
        return database.get(id);
    }

    public List<User> find(List<Long> ids) {
        List<User> users = new ArrayList<>();
        for (Long id : ids) {
            users.add(database.get(id));
        }
        return users;
    }

    public User add(User user) {
        initRecords += 1;
        user.setId(initRecords);
        database.put(initRecords, user);
        if (listener != null) listener.onCountChange(count());
        return user;
    }

    public synchronized User delete(Long id) {
        if (id <= 8) {
            throw new IllegalArgumentException("该用户为保留用户,不能删除");
        }

        User user = database.get(id);
        if (user != null) {
            database.remove(id);
            if (listener != null) listener.onCountChange(count());
            return user;
        } else throw new NullPointerException("Id不存在/该用户为保留用户,不能删除");
    }

    public User update(User user) {
        Long id = user.getId();
        if (null == id) {
            throw new NullPointerException("Id不存在");
        } else {
            User dbUser = database.get(user.getId());
            if (Objects.nonNull(user.getName())) {
                dbUser.setName(user.getName());
            }

            if (Objects.nonNull(user.getAge())) {
                dbUser.setAge(user.getAge());
            }

            if (Objects.nonNull(user.getBirthday())) {
                dbUser.setBirthday(user.getBirthday());
            }

            if (Objects.nonNull(user.getUsername())) {
                dbUser.setUsername(user.getUsername());
            }

            if (Objects.nonNull(user.getGender())) {
                dbUser.setGender(user.getGender());
            }

            if (Objects.nonNull(user.getSalary())) {
                dbUser.setSalary(user.getSalary());
            }

            if (Objects.nonNull(user.getMoney())) {
                dbUser.setMoney(user.getMoney());
            }

            if (Objects.nonNull(user.getMarried())) {
                dbUser.setMarried(user.getMarried());
            }

            dbUser.setUpdateTime(Instant.now());

            database.put(id, dbUser);
            return dbUser;
        }
    }

    public Long count() {
        return (long) database.size();
    }

    public void setCountChangeListener(CountChangeListener listener) {
        this.listener = listener;
    }

    public User login(User user) {
        String username = user.getUsername();
        String password = user.getPassword();
        for (long i = 1; i <= 8; i++) {
            User x = database.get(i);
            if (x.getUsername().equals(username) && x.getPassword().equals(password)) {
                return x;
            }
        }
        return null;
    }

    public User queryByUsername(String username) {
        if (databaseCache.isEmpty()) {
            databaseCache.putAll(database.values().stream().collect(Collectors.toMap(User::getUsername, u -> u)));
        }
        return databaseCache.get(username);
    }

    public static void main(String[] args) {
        UserDAO dao = new UserDAO();
        System.out.println(dao.queryForPage(1, 10).getResult());
    }
}
